/*     */ package com.zimbra.cs.datasource;
/*     */ 
/*     */ import com.zimbra.common.localconfig.KnownKey;
/*     */ import com.zimbra.common.localconfig.LC;
/*     */ import com.zimbra.common.net.SocketFactories;
/*     */ import com.zimbra.common.service.RemoteServiceException;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.account.DataSource;
/*     */ import com.zimbra.cs.filter.RuleManager;
/*     */ import com.zimbra.cs.mailbox.DeliveryContext;
/*     */ import com.zimbra.cs.mailbox.Flag;
/*     */ import com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException;
/*     */ import com.zimbra.cs.mailbox.Mailbox;
/*     */ import com.zimbra.cs.mailbox.Message;
/*     */ import com.zimbra.cs.mailclient.CommandFailedException;
/*     */ import com.zimbra.cs.mailclient.MailConfig.Security;
/*     */ import com.zimbra.cs.mailclient.ParseException;
/*     */ import com.zimbra.cs.mailclient.pop3.ContentInputStream;
/*     */ import com.zimbra.cs.mailclient.pop3.Pop3Config;
/*     */ import com.zimbra.cs.mailclient.pop3.Pop3Connection;
/*     */ import com.zimbra.cs.mime.ParsedMessage;
/*     */ import com.zimbra.soap.type.DataSource.ConnectionType;
/*     */ import java.io.IOException;
/*     */ import java.util.Date;
/*     */ import java.util.List;
/*     */ import java.util.Set;
/*     */ import java.util.regex.Matcher;
/*     */ import java.util.regex.Pattern;
/*     */ import javax.mail.MessagingException;
/*     */ import javax.mail.internet.MimeMessage;
/*     */ import javax.security.auth.login.LoginException;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public class Pop3Sync
/*     */   extends MailItemImport
/*     */ {
/*     */   private final Pop3Connection connection;
/*     */   private final boolean indexAttachments;
/*  55 */   private static final Log LOG = ZimbraLog.datasource;
/*     */   
/*     */ 
/*  58 */   private static final Pattern PATTERN_ZIMBRA_UID = Pattern.compile("(\\d+)\\.([^\\.]+)");
/*     */   
/*     */   public Pop3Sync(DataSource ds) throws ServiceException
/*     */   {
/*  62 */     super(ds);
/*  63 */     this.connection = new Pop3Connection(newPop3Config(ds));
/*  64 */     this.indexAttachments = this.mbox.attachmentsIndexingEnabled();
/*     */   }
/*     */   
/*     */   private Pop3Config newPop3Config(DataSource ds) {
/*  68 */     Pop3Config config = new Pop3Config();
/*  69 */     config.setHost(ds.getHost());
/*  70 */     config.setPort(ds.getPort().intValue());
/*  71 */     config.setAuthenticationId(ds.getUsername());
/*  72 */     config.setSecurity(getSecurity(ds.getConnectionType()));
/*  73 */     if (ds.isDebugTraceEnabled()) {
/*  74 */       config.setLogger(SyncUtil.getTraceLogger(ZimbraLog.pop_client, ds.getId()));
/*     */     }
/*  76 */     config.setSocketFactory(SocketFactories.defaultSocketFactory());
/*  77 */     config.setSSLSocketFactory(SocketFactories.defaultSSLSocketFactory());
/*  78 */     config.setConnectTimeout(ds.getConnectTimeout(LC.javamail_pop3_timeout.intValue()).intValue());
/*  79 */     config.setReadTimeout(ds.getReadTimeout(LC.javamail_pop3_timeout.intValue()));
/*  80 */     return config;
/*     */   }
/*     */   
/*     */   private MailConfig.Security getSecurity(DataSource.ConnectionType type)
/*     */   {
/*  85 */     if (type == null) {
/*  86 */       type = DataSource.ConnectionType.cleartext;
/*     */     }
/*  88 */     switch (type)
/*     */     {
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */     case cleartext: 
/*  95 */       return (!this.dataSource.isOffline()) && (LC.javamail_pop3_enable_starttls.booleanValue()) ? MailConfig.Security.TLS_IF_AVAILABLE : MailConfig.Security.NONE;
/*     */     
/*     */     case ssl: 
/*  98 */       return MailConfig.Security.SSL;
/*     */     case tls: 
/* 100 */       return MailConfig.Security.TLS;
/*     */     case tls_if_available: 
/* 102 */       return MailConfig.Security.TLS_IF_AVAILABLE; }
/*     */     
/* 104 */     return MailConfig.Security.NONE;
/*     */   }
/*     */   
/*     */   public synchronized void test()
/*     */     throws ServiceException
/*     */   {
/* 110 */     validateDataSource();
/*     */     try {
/* 112 */       connect();
/* 113 */       if ((this.dataSource.leaveOnServer()) && (!hasUIDL())) {
/* 114 */         throw RemoteServiceException.POP3_UIDL_REQUIRED();
/*     */       }
/* 116 */       this.connection.quit();
/*     */     } catch (IOException e) {
/* 118 */       throw ServiceException.FAILURE("Unable to connect to POP3 server: " + this.dataSource, e);
/*     */     }
/*     */     finally {
/* 121 */       this.connection.close();
/*     */     }
/*     */   }
/*     */   
/*     */   public synchronized void importData(List<Integer> folderIds, boolean fullSync) throws ServiceException
/*     */   {
/* 127 */     validateDataSource();
/* 128 */     connect();
/*     */     try {
/* 130 */       if (this.connection.getMessageCount() > 0) {
/* 131 */         if (this.dataSource.leaveOnServer()) {
/* 132 */           fetchAndRetainMessages();
/*     */         } else {
/* 134 */           fetchAndDeleteMessages();
/*     */         }
/*     */       }
/* 137 */       this.connection.quit();
/*     */     } catch (ServiceException e) {
/* 139 */       throw e;
/*     */     } catch (Exception e) {
/* 141 */       throw ServiceException.FAILURE("Synchronization of POP3 folder failed", e);
/*     */     }
/*     */     finally {
/* 144 */       this.connection.close();
/*     */     }
/*     */   }
/*     */   
/*     */   private void connect() throws ServiceException {
/* 149 */     if (!this.connection.isClosed()) return;
/*     */     try {
/* 151 */       this.connection.connect();
/*     */       try {
/* 153 */         this.connection.login(this.dataSource.getDecryptedPassword());
/*     */       } catch (CommandFailedException e) {
/* 155 */         throw new LoginException(e.getError());
/*     */       }
/*     */     } catch (Exception e) {
/* 158 */       this.connection.close();
/* 159 */       throw ServiceException.FAILURE("Unable to connect to POP3 server: " + this.dataSource, e);
/*     */     }
/*     */   }
/*     */   
/*     */   private boolean hasUIDL() throws IOException
/*     */   {
/* 165 */     if (this.connection.hasCapability("UIDL")) {
/* 166 */       return true;
/*     */     }
/*     */     
/*     */     try
/*     */     {
/* 171 */       if (this.connection.getMessageCount() > 0)
/*     */       {
/* 173 */         this.connection.getMessageUid(1);
/*     */       } else {
/* 175 */         this.connection.getMessageUids();
/*     */       }
/*     */     } catch (CommandFailedException e) {
/* 178 */       return false;
/*     */     }
/* 180 */     return true;
/*     */   }
/*     */   
/*     */   private void fetchAndDeleteMessages() throws Exception
/*     */   {
/* 185 */     Integer[] sizes = this.connection.getMessageSizes();
/*     */     
/* 187 */     LOG.info("Found %d new message(s) on remote server", new Object[] { Integer.valueOf(sizes.length) });
/* 188 */     IOExceptionHandler.getInstance().resetSyncCounter(this.mbox);
/* 189 */     for (int msgno = sizes.length; msgno > 0; msgno--) {
/* 190 */       LOG.debug("Fetching message number %d", new Object[] { Integer.valueOf(msgno) });
/* 191 */       IOExceptionHandler.getInstance().trackSyncItem(this.mbox, msgno);
/*     */       try {
/* 193 */         fetchAndAddMessage(msgno, sizes[(msgno - 1)].intValue(), null, true);
/*     */       } catch (Exception e) {
/* 195 */         if (!IOExceptionHandler.getInstance().isRecoverable(this.mbox, msgno, "pop sync fail", e))
/*     */           break label116;
/*     */       }
/* 198 */       continue;
/*     */       label116:
/* 200 */       throw e;
/*     */       
/* 202 */       checkIsEnabled();
/* 203 */       this.connection.deleteMessage(msgno);
/*     */     }
/* 205 */     IOExceptionHandler.getInstance().checkpointIOExceptionRate(this.mbox);
/*     */   }
/*     */   
/*     */   private void fetchAndRetainMessages() throws Exception
/*     */   {
/* 210 */     String[] uids = this.connection.getMessageUids();
/* 211 */     Set<String> existingUids = PopMessage.getMatchingUids(this.dataSource, uids);
/* 212 */     int count = uids.length - existingUids.size();
/*     */     
/* 214 */     LOG.info("Found %d new message(s) on remote server", new Object[] { Integer.valueOf(count) });
/* 215 */     if (count == 0) {
/* 216 */       return;
/*     */     }
/* 218 */     IOExceptionHandler.getInstance().resetSyncCounter(this.mbox);
/* 219 */     boolean checkForSelfPopping = true;
/* 220 */     for (int msgno = uids.length; msgno > 0; msgno--) {
/* 221 */       String uid = uids[(msgno - 1)];
/*     */       
/* 223 */       if (!existingUids.contains(uid)) {
/* 224 */         if (checkForSelfPopping)
/*     */         {
/* 226 */           if (poppingSelf(uid)) {
/* 227 */             throw ServiceException.INVALID_REQUEST("User attempted to import messages from his own mailbox", null);
/*     */           }
/*     */           
/* 230 */           checkForSelfPopping = false;
/*     */         }
/* 232 */         LOG.debug("Fetching message with uid %s", new Object[] { uid });
/* 233 */         IOExceptionHandler.getInstance().trackSyncItem(this.mbox, msgno);
/*     */         
/*     */ 
/*     */         try
/*     */         {
/* 238 */           fetchAndAddMessage(msgno, this.connection.getMessageSize(msgno), uid, false);
/*     */         } catch (Exception e) {
/* 240 */           if (!IOExceptionHandler.getInstance().isRecoverable(this.mbox, msgno, "pop sync fail", e))
/*     */           {
/*     */ 
/*     */ 
/* 244 */             throw e; }
/*     */         }
/*     */       }
/*     */     }
/* 248 */     IOExceptionHandler.getInstance().checkpointIOExceptionRate(this.mbox);
/*     */   }
/*     */   
/*     */   private void fetchAndAddMessage(int msgno, int size, String uid, boolean allowFilterToMountpoint) throws ServiceException, IOException
/*     */   {
/* 253 */     ContentInputStream cis = null;
/* 254 */     MessageContent mc = null;
/* 255 */     checkIsEnabled();
/*     */     try {
/* 257 */       cis = this.connection.getMessage(msgno);
/* 258 */       mc = MessageContent.read(cis, size);
/* 259 */       ParsedMessage pm = mc.getParsedMessage(null, this.indexAttachments);
/* 260 */       if (pm == null) {
/* 261 */         LOG.warn("Empty message body for UID %d. Must be ignored.", new Object[] { uid });
/*     */       }
/*     */       else {
/* 264 */         Message msg = null;
/*     */         try
/*     */         {
/* 267 */           Date sentDate = pm.getMimeMessage().getSentDate();
/* 268 */           if (sentDate == null) {
/* 269 */             LOG.warn("null sent date; probably due to parse error. Date header value: [%s]", new Object[] { pm.getMimeMessage().getHeader("Date", null) });
/*     */           }
/* 271 */           pm.setReceivedDate(sentDate != null ? sentDate.getTime() : System.currentTimeMillis());
/*     */         } catch (MessagingException e) {
/* 273 */           LOG.warn("unable to get sent date from parsed message due to exception, must use current time", e);
/* 274 */           pm.setReceivedDate(System.currentTimeMillis());
/*     */         }
/* 276 */         DeliveryContext dc = mc.getDeliveryContext();
/* 277 */         if (isOffline()) {
/* 278 */           msg = addMessage(null, pm, size, this.dataSource.getFolderId(), Flag.BITMASK_UNREAD, dc);
/*     */         } else {
/* 280 */           Integer localId = getFirstLocalId(RuleManager.applyRulesToIncomingMessage(null, this.mbox, pm, size, this.dataSource.getEmailAddress(), dc, this.dataSource.getFolderId(), true, allowFilterToMountpoint));
/*     */           
/*     */ 
/*     */ 
/* 284 */           if (localId != null) {
/* 285 */             msg = this.mbox.getMessageById(null, localId.intValue());
/*     */           }
/*     */         }
/* 288 */         if ((msg != null) && (uid != null)) {
/* 289 */           PopMessage msgTracker = new PopMessage(this.dataSource, msg.getId(), uid);
/* 290 */           msgTracker.add();
/*     */         }
/*     */       }
/* 293 */     } catch (CommandFailedException e) { LOG.warn("Error fetching message number %d: %s", new Object[] { Integer.valueOf(msgno), e.getMessage() });
/*     */     } finally {
/* 295 */       if (cis != null) {
/*     */         try {
/* 297 */           cis.close();
/*     */         } catch (ParseException pe) {
/* 299 */           LOG.error("ParseException while closing ContentInputStream. Assuming cis is effectively closed", pe);
/*     */         }
/*     */       }
/* 302 */       if (mc != null) {
/* 303 */         mc.cleanup();
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   private boolean poppingSelf(String uid) throws ServiceException
/*     */   {
/* 310 */     Matcher matcher = PATTERN_ZIMBRA_UID.matcher(uid);
/* 311 */     if (!matcher.matches()) {
/* 312 */       return false;
/*     */     }
/*     */     
/*     */     int itemId;
/*     */     try
/*     */     {
/* 318 */       itemId = Integer.parseInt(matcher.group(1));
/*     */     } catch (NumberFormatException e) {
/* 320 */       return false;
/*     */     }
/* 322 */     String digest = matcher.group(2);
/*     */     Message msg;
/*     */     try {
/* 325 */       msg = this.mbox.getMessageById(null, itemId);
/*     */     } catch (MailServiceException.NoSuchItemException e) {
/* 327 */       return false;
/*     */     }
/* 329 */     return digest.equals(msg.getDigest());
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/datasource/Pop3Sync.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */